#pragma once

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <string.h>
#include <sstream>
#include <memory>
#include <stdexcept>
#include <array>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <unordered_map>
#include <functional>

// CPU信息结构体
struct CPUInfo
{
    std::string processor_;              // CPU 的逻辑编号
    std::string vendor_id_;              // CPU 制造商的标识符
    std::string cpu_family_;             // CPU 所属的家族编号
    std::string model_;                  // CPU 型号编号
    std::string model_name_;             // CPU 的完整型号名称
    std::string stepping_;               // CPU 的步进编号
    std::string microcode_;              // CPU 微代码版本号
    std::string cpu_freq_;               // CPU 当前的运行频率，单位为 MHz
    std::string cache_size_;             // CPU 缓存的大小，单位为KB
    std::string physical_id_;            // CPU 的标识符
    std::string siblings_;               // 同一物理 CPU 上的逻辑处理器数量
    std::string core_id_;                // 每个物理核心的唯一标识符
    std::string cpu_cores_;              // 每个物理 CPU 中的核心数量，不包括超线程产生的逻辑核心
    std::string clflush_size_;           // 缓存行刷新（Cache Line Flush）操作所涉及的缓存行大小
};

// 磁盘信息结构体
struct DiskStatsInfo
{
    std::string major_device_num_;          // 主设备号，标识设备类型
    std::string minor_device_num_;          // 次设备号，用于在同一主设备类型下进一步区分具体的设备实例，主设备号和次设备号共同唯一标识系统中的一个块设备
    std::string device_name_;               // 磁盘名称，对应/dev目录下的设备文件名
    std::string read_completed_;            // 从设备成功完成的读操作次数，每次成功读取数据块，该计数增加
    std::string read_merged_;               // 合并读操作次数
    std::string read_sectors_;              // 从设备读取的扇区总数
    std::string read_time_spent_;           // 累计花费在读操作上的时间，单位毫秒
    std::string write_completed_;           // 向设备成功完成的写操作次数，每次成功写入数据块，该计数增加
    std::string write_merged_;              // 合并写操作次数
    std::string write_sectors_;             // 向设备写入的扇区总数
    std::string write_time_spent_;          // 累计花费在写操作上的时间，单位毫秒
};

// BIOS信息结构体，细节信息通过dmidecode命令获取到的
struct BIOSMoreInfo
{
    // 基础信息
    std::string     vendor_;                    // BIOS供应商
    std::string     version_;                   // BIOS版本
    std::string     date_;                      // BIOS发布日期
    
    // 细节信息
    std::string   address_;                     // BIOS 在内存中的物理地址(十六进制)
    std::string   runtime_size_;                // BIOS运行时占用的内存大小，单位为bytes
    std::string   rom_size_;                    // 存储 BIOS 固件的 ROM（只读存储器）容量，单位为kB
    std::string   bios_revision_;               // BIOS的修订版本号，可能与Version不同
    std::string   firmware_revisioin_;          // 主板固件版本号
};

// 主板信息结构体，细节信息通过dmidecode命令获取
struct BaseboardMoreInfo
{
    // 基础信息
    std::string     vendor_;                    // 主板供应商
    std::string     version_;                   // 主板版本号
    std::string     name_;                      // 主板型号
    std::string     asset_tag_;                 // 主板资产标签

    // 细节信息
    std::string     serial_num_;                // 主板序列号
    std::string     location_in_chassis_;       // 主板在机箱中的位置
    std::string     type_;                      // 主板类型
};

// 检测当前运行环境是否可以使用dmidecode命令
#ifndef IsUseDmidecodeCommand
#define IsUseDmidecodeCommand()\
    bool is_use_dmidecode = false;\
    \
    if(geteuid() == 0 && FindDmidecodePath())\
        is_use_dmidecode = true;\
    else\
        std::cout << "当前运行环境不支持dmidecode命令, 只获取基本信息" << std::endl;
#endif

// 根据:进行字符串切割并去除两端空格得到key和value字符串
#ifndef SplitRemoveWhite
#define SplitRemoveWhite(_line)\
    size_t pos = _line.find(':');\
    \
    if (pos == std::string::npos)\
        return;\
    \
    std::string key = _line.substr(0, pos);\
    std::string value = _line.substr(pos + 1);\
    \
    RemoveWhiteSpace(value);\
    RemoveWhiteSpace(key);
#endif

// Linux下获取CPU的所有信息
class LinuxHardwareInfo
{
public:

    LinuxHardwareInfo();
    ~LinuxHardwareInfo();

    /*
        @brief 获取CPU的所有信息
        @param _cpu_infos 会有多个cpu的信息
    */
    void GetProcessorAllInfo(std::vector<CPUInfo>& _cpu_infos);

    /*
        @brief 获取磁盘的所有信息
        @param _disk_infos 会有多个磁盘信息
    */
    void GetDiskStatsAllInfo(std::vector<DiskStatsInfo>& _disk_infos);

    /*
        @brief 获取BIOS的信息
            1、该接口会先使用demidecode命令获取BIOS更详细的信息，如果检测到系统的demidecode命令无法使用，改用读取文件的方式只获取BISO
                基本信息！！！
            2、该接口返回true的情况为：Linux系统已经安装demidecode命令，并且运行程序的用户为root用户
        @param _bios_info BIOS信息结构体
        @return true表示demidecode命令可以使用，获取到了更详细的信息；false说明当前运行环境的demidecode命令无法使用，只能获取基本信息。
    */
    bool GetBIOSAllInfo(BIOSMoreInfo& _bios_info);

    /*
        @brief 获取主板的信息
            1、该接口会先使用demidecode命令获取BIOS更详细的信息，如果检测到系统的demidecode命令无法使用，改用读取文件的方式只获取主板
                基本信息！！！
            2、该接口返回true的情况为：Linux系统已经安装demidecode命令，并且运行程序的用户为root用户
        @param _board_info 主板信息结构体
        @return true表示demidecode命令可以使用，获取到了更详细的信息；false说明当前运行环境的demidecode命令无法使用，只能获取基本信息。
    */
    bool GetBoardAllInfo(BaseboardMoreInfo& _board_info);

private:

    /*
        @brief 通过读取文件的方式获取BIOS基本信息
        @param BIOS基本信息
    */
    void GetBIOSBasicInfo(BIOSMoreInfo& _bios_info);

    /*
        @brief 通过读取文件的方式获取主板基本信息
        @param 主板基本信息
    */
    void GetBoardBasicInfo(BaseboardMoreInfo& _board_info);

    /*
        @brief 去除字符串两端的空白字符，包括空格、\t等
        @param _str 源字符串
    */
    void RemoveWhiteSpace(std::string& _str);

    /*
        @brief 解析CPU文件信息的一行
        @param _line 一行字符串
        @param _cpu_info 存储数据的结构体
    */
    void ParseProcessorLine(const std::string& _line, CPUInfo& _cpu_info);

    /*
        @brief 处理BISO信息的一行
        @param _line 一行字符串
        @param _bios_info BISO数据结构体
    */
    void ProcessBIOSLine(const std::string& _line, BIOSMoreInfo& _bios_info);

    /*
        @brief 处理主板信息的一行
        @param _line 一行字符串
        @param _board_info 主板数据结构体
    */
    void ProcessBoardLine(const std::string& _line, BaseboardMoreInfo& _board_info);

    /*
        @brief 检查 dmidecode 是否存在并返回其完整路径
        @return true表示存在，false表示不存在
    */
    bool FindDmidecodePath();

    /*
        @brief 执行命令并捕获输出
        @param cmd 命令
        @return 输出的字符串
    */
    std::string GetStrByCommand(const char* _cmd);

    /*
        @brief 按照换行符进行切割
        @param _str 源字符串
        @return 切割后的集合
    */
    std::vector<std::string> SplitByNewline(const std::string& _str);

    /*
        @brief 更新主板结构体处理器
        @param _key 结构体成员对应的key值
        @param value 结构体成员值
    */
    void UpdateBoardInfo(BaseboardMoreInfo& _board_info, const std::string& _key, const std::string& _value);

    /*
        @brief 更新BIOS结构体处理器
        @param _key 结构体成员对应的key值
        @param value 结构体成员值
    */
    void UpdateBIOSInfo(BIOSMoreInfo& _bios_info, const std::string& _key, const std::string& _value);

    /*
        @brief 更新CPU结构体处理器
        @param _key 结构体成员对应的key值
        @param value 结构体成员值
    */
    void UpdateCpuInfo(CPUInfo& _cpu_info, const std::string& _key, const std::string& _value);

private:

    // 成员指针类型别名
    using BoardMemberPtr = std::string BaseboardMoreInfo::*;

    using BIOSMemberPtr = std::string BIOSMoreInfo::*;

    using CpuMemberPtr = std::string CPUInfo::*;

    const std::string cpu_file_;        // Linux系统下cpu信息的存储路径

    const std::string disk_file_;       // Linux系统下磁盘信息的存储路径

    const std::string bios_dir_;                             // Linux系统下存放BIOS信息的目录
    const std::vector<std::string>  bios_files_name_;        // Linux系统下存放BIOS信息的所有文件名

    const std::string board_dir_;                             // Linux系统下存放主板信息的目录
    const std::vector<std::string>  board_files_name_;        // Linux系统下存放主板信息的所有文件名

    const std::vector<std::string>  dmidecode_command_path_; // Linux系统下常见的demidecode命令的完整路径，检测系统是否安装该命令会用到

    std::unordered_map<std::string, std::function<void(std::string)>>   process_board_value_map_;           // 给主板信息结构体赋值    
};